上一章節我們有簡單的在提一下軟體架構設計的兩個基本原則 :
這裡我們在提一次。然後接下來我們這篇文章將要來談談 SOLID 中的 OCP 開發一封閉原則,也是為了達成上面這兩件原則。
根據 《 Clean Architecture 》這本書裡所書寫的定義為 :
一個軟體應該對於擴展是開放的,但對於修改是封閉的
這個比較白話文的來說我覺得是 :
當有新功能時,是可以用類式 plugin 的方式加新功能,而不是去修改原本的程式
這個違反這個原則,事實上在我們寫的程式碼中非常的常見,你想想 ~ 是不是在程式中,有很多 function 裡會使用 if else 情境來判斷要用什麼來處理,例如訂單可能會根據不同產品來進行一些對同處理,這時我們是不是最直覺的寫法就是加個 if 呢 ?
我個人是認為在開發初起,我們很難預測未來的變動,所以這時簡單用個 if 來處理,就我個人是覺得可以接受,但是如果之後又來 ~ 那我就覺得真的可能考慮重構了呢。
這裡只要記好一件事情 :
開放封閉原則在軟體架構存在的目的,就是在於怕你要改時,會動到原本的程式碼,而導致可能的出錯
但我小聲的說…如果有寫測試,那事實上不太會影響…… 在某些小型專案上。
但是這個如果拉到大型專案上,一直用 if else 又或是一直在原本的地方加功能,會哭的不是你,而是後人,活生生的血淚屎。
首先來來個基本版,就是一個專門用來處理訂單的類別,然後他有個方法可以計算這個訂單的費用。
// bad
class OrderFeeCalculator{
constructor(order){
this.order = order
}
calcFee(){
let fee = 0
fee = 0.1 * this.order.price
return fee
}
}
然後這時有個新需求,我們訂單的類型為『 文章 ARTICLE 』的話,則費用只要價格的 5% 就好,那這時通常第一直覺會直接改成如下 :
// Bad 2
class OrderFeeCalculator{
constructor(order){
this.order = order
}
calcFee(){
let fee = 0
if(this.order.category === 'ARTICLE'){
fee = 0.05 * this.order.price
}else{
fee = 0.1 * this.order.price
}
return fee
}
}
這一題事實上可以用一些設計模式來解決。
這樣如果某一個產品需要特殊的計算,那就只要在對應的 strategy 裡修改運算就 ok 囉,而不用怕會影響到其它產品的運算。
// Good
class OrderFeeCalculator{
constructor(order){
this.order = order
}
calcFee(){
let fee = 0
fee = _productFeeStrategyFactory[this.order.category].calcFee(this.order)
return fee
}
_productOrderFeeFactor(category){
switch (category) {
case 'ARTICLE':
return new ArticleFeeStrategy()
case 'COURSE':
return new CorseFeeStrategy()
}
}
}
class ArticleFeeStrategy{
calcFee(order){
return this.order.price * 0.05
}
}
class CorseFeeStrategy{
calcFee(order){
return this.order.price * 0.1
}
}
事實上我仔細想想,不少的設計模式都是為了來達到『 開放封閉原則 』,例如 :
老樣子,來問一下三個問題,來加深記憶
我覺得分層架構也有一定原因是因為這個原則而產生的,我們不希望 Presenters 層級的修改,影響到 Controller。
分層架構、與一些設計模式都有連結,這些都是可以讓我們們更接近 OCP 的方法。